package org.ghost.springboot.demo.config;

import com.baomidou.mybatisplus.MybatisConfiguration;
import com.baomidou.mybatisplus.MybatisXMLLanguageDriver;
import com.baomidou.mybatisplus.entity.GlobalConfiguration;
import com.baomidou.mybatisplus.plugins.OptimisticLockerInterceptor;
import com.baomidou.mybatisplus.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.plugins.PerformanceInterceptor;
import com.baomidou.mybatisplus.plugins.SqlExplainInterceptor;
import com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean;
import org.apache.ibatis.plugin.Interceptor;
import org.ghost.springboot.demo.component.mybatis.MaxRowsLimitInterceptor;
import org.mybatis.spring.boot.autoconfigure.MybatisProperties;
import org.mybatis.spring.boot.autoconfigure.SpringBootVFS;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;

import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

@Configuration
public class CommonConfig {
    @Autowired
    private MybatisProperties properties;

    @ConditionalOnMissingBean
    public NamedParameterJdbcTemplate createNamedParameterJdbcTemplate(DataSource dataSource) {
        return new NamedParameterJdbcTemplate(dataSource);
    }

    @Bean
    public MybatisSqlSessionFactoryBean mybatisSqlSessionFactoryBean(DataSource dataSource) {
        MybatisSqlSessionFactoryBean mybatisPlus = new MybatisSqlSessionFactoryBean();
        mybatisPlus.setDataSource(dataSource);
        mybatisPlus.setVfs(SpringBootVFS.class);

        // MP 全局配置，直接使用默认配置(MYSQL，主键使用雪花算法)
        GlobalConfiguration globalConfig = new GlobalConfiguration();
        mybatisPlus.setGlobalConfig(globalConfig);

        // 使用MybatisPlus设置, 忽略配置文件configuration中除了variables以外的其他属性
        MybatisConfiguration mybatisConfiguration = new MybatisConfiguration();
        mybatisConfiguration.setDefaultScriptingLanguage(MybatisXMLLanguageDriver.class);
        mybatisConfiguration.setMapUnderscoreToCamelCase(true);
        mybatisPlus.setConfiguration(mybatisConfiguration);
        // 插件设置
        setPlugins(mybatisPlus);
        // xmlMapper文件读入
        mybatisPlus.setMapperLocations(this.properties.resolveMapperLocations());
        // 其他属性设置
        mybatisPlus.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
        mybatisPlus.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());

        return mybatisPlus;
    }

    private void setPlugins(MybatisSqlSessionFactoryBean mybatisPlus) {
        //自定义拦截器
        MaxRowsLimitInterceptor maxRowsLimitInterceptor = new MaxRowsLimitInterceptor();
        if (this.properties != null && this.properties.getConfiguration() != null && this.properties.getConfiguration().getVariables() != null) {
            mybatisPlus.setConfigurationProperties(this.properties.getConfiguration().getVariables());
            maxRowsLimitInterceptor.setProperties(this.properties.getConfiguration().getVariables());
        }

        boolean paginationInterceptor = true;
        boolean performanceInterceptor = false;
        boolean optimisticLockerInterceptor = true;
        boolean sqlExplainInterceptor = false;
        if (this.properties != null && this.properties.getConfiguration() != null && this.properties.getConfiguration().getVariables() != null) {
            paginationInterceptor = Boolean.parseBoolean(this.properties.getConfiguration().getVariables().getOrDefault("paginationInterceptor", Boolean.TRUE).toString());
            performanceInterceptor = Boolean.parseBoolean(this.properties.getConfiguration().getVariables().getOrDefault("performanceInterceptor", Boolean.FALSE).toString());
            optimisticLockerInterceptor = Boolean.parseBoolean(this.properties.getConfiguration().getVariables().getOrDefault("optimisticLockerInterceptor", Boolean.TRUE).toString());
            sqlExplainInterceptor = Boolean.parseBoolean(this.properties.getConfiguration().getVariables().getOrDefault("sqlExplainInterceptor", Boolean.FALSE).toString());
        }

        List<Interceptor> interceptorList = new ArrayList<Interceptor>();
        interceptorList.add(maxRowsLimitInterceptor);
        if (paginationInterceptor) {
            interceptorList.add(getPaginationInterceptor());
        }
        if (performanceInterceptor) {
            interceptorList.add(getPerformanceInterceptor());
        }
        if (optimisticLockerInterceptor) {
            interceptorList.add(getOptimisticLockerInterceptor());
        }
        if (sqlExplainInterceptor) {
            interceptorList.add(getSqlExplainInterceptor());
        }
        mybatisPlus.setPlugins(interceptorList.stream().filter(Objects::nonNull).toArray(Interceptor[]::new));
    }

    /**
     * 分页插件
     *
     * @return
     */
    private PaginationInterceptor getPaginationInterceptor() {
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        paginationInterceptor.setDialectType("mysql");
        return paginationInterceptor;
    }

    /**
     * 性能分析插件(正式机不要用)
     *
     * @return
     */
    private PerformanceInterceptor getPerformanceInterceptor() {
        PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
        performanceInterceptor.setFormat(true);
        performanceInterceptor.setWriteInLog(true);
        performanceInterceptor.setMaxTime(3000L);
        return performanceInterceptor;
    }

    /**
     * 乐观锁插件com.baomidou.mybatisplus.annotations.Version注解
     *
     * @return
     */
    private OptimisticLockerInterceptor getOptimisticLockerInterceptor() {
        return new OptimisticLockerInterceptor();
    }

    /**
     * 执行计划(正式机不要用)
     *
     * @return
     */
    private SqlExplainInterceptor getSqlExplainInterceptor() {
        SqlExplainInterceptor sqlExplainInterceptor = new SqlExplainInterceptor();
        sqlExplainInterceptor.setStopProceed(true);
        return sqlExplainInterceptor;
    }
}
